import { AlignType, ClassName, CoordinateSystem, CtrlType, Events } from "@/Constants";
import { IPixelPos, IPoint, IRelativePos, ISize, Style } from "../../Interface";
import { Utils } from "../../utils";
import Feature from "../Feature";

// 矩形元素
class Rect extends Feature {

    protected _radius: number = 0; // 元素的圆角大小, 一般Rect类型用
    get radius() { return this._radius }
    set radius(val) {
        this._radius = val;
        this.pointArr = Utils.getRoundRectPoints(this.position, this.size, this.radius).map(p => Utils.getRotatePnt(Feature.getCenterPos(this.pointArr), p, this.angle));
    }
    borderStyle: Style = {
        lineWidth: this.lineWidth,
        color: this.strokeStyle,
    }
    border: AlignType | AlignType[] = []
    position: IRelativePos = { x: 0, y: 0 }  // 元素包围盒的中心点, 一般Rect类型用
    size: ISize = { width: 0, height: 0 }  // 元素包围盒的宽高

    constructor(x: number = 0, y: number = 0, width: number = 15, height: number = 15, radius = 0) {   // 相对坐标
        const pointArr = Utils.getRoundRectPoints({ x, y }, { width, height }, radius)   // 能生成圆角矩形所有点集合更好
        super(pointArr);
        this.className = ClassName.RECT;
        this.position.x = x;
        this.position.y = y;
        this.size.width = width;
        this.size.height = height;
        this.radius = radius;
        this.isClosePath = true;
        this.ctrlTypes = [CtrlType.WIDTH_CTRL, CtrlType.HEIGHT_CTRL, CtrlType.SIZE_CTRL, CtrlType.ANGLE_CTRL];  // 可用的控制点类型有哪些
    }

    draw(ctx: CanvasRenderingContext2D, pointArr: IPixelPos[], lineWidth: number, lineDashArr: number[]): void | Path2D {
        if (!(this.border instanceof Array)) {
            const borderPnts = Utils.getBorderOfRect(Feature.getCenterPos(pointArr), this.getBboxSize(pointArr), this.border);
            ctx.save()
            this.rotateCtx(ctx, Feature.getCenterPos(pointArr));
            const path = new Path2D();
            borderPnts.forEach((p, i) => {
                if (i == 0) {
                    path.moveTo(p.x, p.y)
                } else {
                    path.lineTo(p.x, p.y)
                }
            })
            ctx.lineWidth = Feature.Render.getRatioSize(this.borderStyle.lineWidth || 1);
            ctx.strokeStyle = this._getFillStyle(this.borderStyle.color || '', ctx);
            if (this.borderStyle.lineDashArr && this.borderStyle.lineDashArr.length == 2) ctx.setLineDash([Feature.Render.getRatioSize(this.borderStyle.lineDashArr[0]), Feature.Render.getRatioSize(this.borderStyle.lineDashArr[1])]);
            ctx.stroke(path);
            ctx.restore()
        }
        return super.draw(ctx, pointArr, lineWidth, lineDashArr);
    }

    setPos(x: number = this.position.x, y: number = this.position.y) {
        this.translate(x - this.position.x, y - this.position.y)
    }

    setSize(width: number = this.size.width, height: number = this.size.height, anchor?: IPoint) {
        if (anchor) {
            if (this.angle) {
                this.pointArr = this.pointArr.map(p => Utils.getRotatePnt(anchor || Feature.getCenterPos(this.pointArr), p, -this.angle));
            }
            const offsetX = width / 2 - this.size.width / 2
            const offsetY = height / 2 - this.size.height / 2
            this.translate(offsetX, offsetY)
        }
        this.size.width = width;
        this.size.height = height;
        this.pointArr = Utils.getRoundRectPoints(this.position, this.size, this.radius).map(p => Utils.getRotatePnt(anchor || Feature.getCenterPos(this.pointArr), p, this.angle));
        this.dispatch(new CustomEvent(Events.RESIZE, { detail: { target: this } }))
    }

    // 获取矩形的宽度，包括旋转，不是包围盒
    getBboxSize(pointArr: IPoint[] = this.pointArr) {
        const rPointArr = this.getPointArr(pointArr, -this.angle, Feature.getCenterPos(pointArr));
        const rectPnts = Feature.getRectWrapPoints(rPointArr);
        let leftTop = rectPnts[0];
        let rightTop = rectPnts[1];
        let rightBottom = rectPnts[2];
        let leftBottom = rectPnts[3];
        // if (!this.isHorizonalRevert && !this.isVerticalRevert) {
        //     leftTop = rectPnts[0]
        // }
        // if (this.isHorizonalRevert && this.isVerticalRevert) {
        //     leftTop = rectPnts[2]
        // }
        // if (this.isHorizonalRevert && !this.isVerticalRevert) {
        //     leftTop = rectPnts[1]
        // }
        // if (!this.isHorizonalRevert && this.isVerticalRevert) {
        //     leftTop = rectPnts[3]
        // }
        return {
            leftTop,
            rightTop,
            rightBottom,
            leftBottom,
            width: Utils.getLenOfTwoPnts(rectPnts[0], rectPnts[1]),
            height: Utils.getLenOfTwoPnts(rectPnts[0], rectPnts[3]),
        }
    }

    toTopAlign(features: Feature[] = this.getBasicChilds()) { // 顶部对齐
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        const pointArr = this.getPointArr(this.pointArr, -angle, center);  // 获取旋转之前的点
        features.forEach(f => {
            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
            const [minX, maxX, minY, maxY] = Feature.getRectWrapExtent(fPointArr, f.margin + this.padding);  // 获取包围盒最左侧的点
            let len = Utils.getLenOfPntToLine({ x: 0, y: minY }, pointArr[0], pointArr[1]);
            const dir = Utils.isPntLeftOrRightOfLine({ x: 0, y: minY }, pointArr[0], pointArr[1]);
            if (dir === 'right') len = -len
            const dx = len * Math.sin(-angle * Math.PI / 180);
            const dy = len * Math.cos(-angle * Math.PI / 180);
            f.translate(-dx, -dy)
        })
        return this;
    }
    toLeftAlign(features: Feature[] = this.getBasicChilds()) { // 左对齐
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        const pointArr = this.getPointArr(this.pointArr, -angle, center);  // 获取group旋转之前的点
        features.forEach((f, i) => {
            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取元素旋转之前的点
            const [minX, maxX, minY, maxY] = Feature.getRectWrapExtent(fPointArr, f.margin + this.padding);  // 获取包围盒最左侧的点;  // 获取包围盒最左侧的点
            let len = Utils.getLenOfPntToLine({ x: minX, y: 0 }, pointArr[0], pointArr[3]);
            const dir = Utils.isPntLeftOrRightOfLine({ x: minX, y: 0 }, pointArr[0], pointArr[3]);
            if (dir === 'left') len = -len
            const dx = len * Math.cos(angle * Math.PI / 180);
            const dy = len * Math.sin(angle * Math.PI / 180);
            f.translate(-dx, -dy)
        })
        return this;
    }
    toBottomAlign(features: Feature[] = this.getBasicChilds()) {
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        const pointArr = this.getPointArr(this.pointArr, -angle, center);  // 获取旋转之前的点
        features.forEach(f => {
            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
            const [minX, maxX, minY, maxY] = Feature.getRectWrapExtent(fPointArr, f.margin + this.padding);  // 获取包围盒最左侧的点;  // 获取包围盒最左侧的点
            let len = Utils.getLenOfPntToLine({ x: 0, y: maxY }, pointArr[2], pointArr[3]);
            const dir = Utils.isPntLeftOrRightOfLine({ x: 0, y: maxY }, pointArr[2], pointArr[3]);
            if (dir === 'right') len = -len
            const dx = len * Math.sin(-angle * Math.PI / 180);
            const dy = len * Math.cos(-angle * Math.PI / 180);
            f.translate(dx, dy)
        })
        return this;
    }
    toRightAlign(features: Feature[] = this.getBasicChilds()) {
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        const pointArr = this.getPointArr(this.pointArr, -angle, center);  // 获取旋转之前的点
        features.forEach(f => {
            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
            const [minX, maxX, minY, maxY] = Feature.getRectWrapExtent(fPointArr, f.margin + this.padding);  // 获取包围盒最左侧的点;  // 获取包围盒最左侧的点
            let len = Utils.getLenOfPntToLine({ x: maxX, y: 0 }, pointArr[1], pointArr[2]);
            const dir = Utils.isPntLeftOrRightOfLine({ x: maxX, y: 0 }, pointArr[1], pointArr[2]);
            if (dir === 'right') len = -len
            const dx = len * Math.cos(angle * Math.PI / 180);
            const dy = len * Math.sin(angle * Math.PI / 180);
            f.translate(dx, dy)
        })
        return this;
    }
    toVerticalAlign(features: Feature[] = this.getBasicChilds()) {
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        features.forEach(f => {
            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
            const fCenter = Feature.getCenterPos(fPointArr)
            const len = center.x - fCenter.x;
            const dx = len * Math.cos(angle * Math.PI / 180);
            const dy = len * Math.sin(angle * Math.PI / 180);
            f.translate(dx, dy)
        })
        return this;
    }
    toHorizonalAlign(features: Feature[] = this.getBasicChilds()) {
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        features.forEach(f => {
            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
            const fCenter = Feature.getCenterPos(fPointArr)
            const len = center.y - fCenter.y;
            const dx = len * Math.sin(-angle * Math.PI / 180);
            const dy = len * Math.cos(-angle * Math.PI / 180);
            f.translate(dx, dy)
        })
        return this;
    }

    // 均匀分布子元素, 两边没有空隙
    toSpaceBetween(features: Feature[] = this.getBasicChilds(), flexFLow = AlignType.HORIZONAL) {
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);

        const pointArr = this.getPointArr(this.pointArr, -angle, center);  // 获取旋转之前的点
        const [leftTop, rightTop, rightBottom, leftBottom] = Feature.getRectWrapPoints(pointArr, this.padding);
        const wrapWidth = Utils.getLenOfTwoPnts(leftTop, rightTop);
        const wrapHeight = Utils.getLenOfTwoPnts(leftTop, leftBottom);

        if (features.length > 1) {
            switch (flexFLow) {
                case AlignType.HORIZONAL:  // 水平方向
                    {
                        let childTotalWidth = 0;   // 子元素宽度总和
                        features.forEach((f, i) => {
                            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
                            const [leftTop, rightTop] = Feature.getRectWrapPoints(fPointArr);
                            childTotalWidth += Utils.getLenOfTwoPnts(leftTop, rightTop); // 计算所有子元素的宽度之和
                        })
                        const spaceLen = (wrapWidth - childTotalWidth) / (features.length - 1)   // 每一段可分配空间
                        if (spaceLen < 0) return
                        features.sort((a, b) => Feature.getRectWrapExtent(a.pointArr)[1] - Feature.getRectWrapExtent(b.pointArr)[0])
                        this.toLeftAlign(features);   // 先左对齐让子元素处于同一起点
                        let lastLen = 0  // 之前所有的子元素宽度+之前所有分配的空间长度

                        features.forEach((f, i) => {
                            const prevFeature = features[i - 1];  // 上一个元素
                            if (prevFeature) {
                                const fPointArr = prevFeature.getPointArr(prevFeature.pointArr, -angle, center); // 获取旋转之前的点
                                const [leftTop, rightTop] = Feature.getRectWrapPoints(fPointArr);
                                lastLen += Utils.getLenOfTwoPnts(leftTop, rightTop) + spaceLen;
                            }
                            const dx = lastLen * Math.cos(angle * Math.PI / 180);
                            const dy = lastLen * Math.sin(angle * Math.PI / 180);
                            f.translate(dx, dy)
                        })
                        break;
                    }
                case AlignType.VERTICAL:  // 垂直方向同理
                    {
                        let childTotalHeight = 0;
                        features.forEach((f, i) => {
                            const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
                            const [leftTop, rightTop, rightBottom, leftBottom] = Feature.getRectWrapPoints(fPointArr);
                            childTotalHeight += Utils.getLenOfTwoPnts(leftTop, leftBottom); // 计算所有子元素的宽度之和
                        })
                        const spaceLen = (wrapHeight - childTotalHeight) / (features.length - 1)   // 每一段可分配空间
                        if (spaceLen < 0) return
                        features.sort((a, b) => Feature.getRectWrapExtent(a.pointArr)[3] - Feature.getRectWrapExtent(b.pointArr)[2])
                        this.toTopAlign(features);
                        let lastLen = 0  // 之前所有的子元素宽度+之前所有分配的空间长度

                        features.forEach((f, i) => {
                            const prevFeature = features[i - 1];  // 上一个元素
                            if (prevFeature) {
                                const fPointArr = prevFeature.getPointArr(prevFeature.pointArr, -angle, center); // 获取旋转之前的点
                                const [leftTop, rightTop, rightBottom, leftBottom] = Feature.getRectWrapPoints(fPointArr);
                                lastLen += Utils.getLenOfTwoPnts(leftTop, leftBottom) + spaceLen;
                            }
                            const dx = lastLen * Math.sin(-angle * Math.PI / 180);
                            const dy = lastLen * Math.cos(-angle * Math.PI / 180);
                            f.translate(dx, dy)
                        })
                        break;
                    }
                default:
                    break;
            }
        }
        return this;
    }
    // 均匀分布子元素, 两边有空隙
    toSpaceAroud(features: Feature[] = this.getBasicChilds(), flexFLow = AlignType.HORIZONAL) {
        const angle = this.angle;
        const center = Feature.getCenterPos(this.pointArr);
        const pointArr = this.getPointArr(this.pointArr, -angle, center);  // 获取旋转之前的点
        const [leftTop, rightTop, rightBottom, leftBottom] = Feature.getRectWrapPoints(pointArr, this.padding);
        const wrapWidth = Utils.getLenOfTwoPnts(leftTop, rightTop);
        const wrapHeight = Utils.getLenOfTwoPnts(leftTop, leftBottom);

        switch (flexFLow) {
            case AlignType.HORIZONAL:
                {
                    let childTotalWidth = 0;
                    features.forEach((f, i) => {
                        const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
                        const [leftTop, rightTop] = Feature.getRectWrapPoints(fPointArr);
                        childTotalWidth += Utils.getLenOfTwoPnts(leftTop, rightTop); // 计算所有子元素的宽度之和
                    })
                    const spaceLen = (wrapWidth - childTotalWidth) / (features.length + 1)   // 每一段可分配空间
                    if (spaceLen < 0) return
                    features.sort((a, b) => Feature.getRectWrapExtent(a.pointArr)[1] - Feature.getRectWrapExtent(b.pointArr)[0])
                    this.toLeftAlign(features);
                    let lastLen = spaceLen  // 之前所有的子元素宽度+之前所有分配的空间长度

                    features.forEach((f, i) => {
                        const prevFeature = features[i - 1];  // 上一个元素
                        if (prevFeature) {
                            const fPointArr = prevFeature.getPointArr(prevFeature.pointArr, -angle, center); // 获取旋转之前的点
                            const [leftTop, rightTop] = Feature.getRectWrapPoints(fPointArr);
                            lastLen += Utils.getLenOfTwoPnts(leftTop, rightTop) + spaceLen;
                        }
                        const dx = lastLen * Math.cos(angle * Math.PI / 180);
                        const dy = lastLen * Math.sin(angle * Math.PI / 180);
                        f.translate(dx, dy)
                    })
                    break;
                }
            case AlignType.VERTICAL:
                {
                    let childTotalHeight = 0;
                    features.forEach((f, i) => {
                        const fPointArr = f.getPointArr(f.pointArr, -angle, center); // 获取旋转之前的点
                        const [leftTop, rightTop, rightBottom, leftBottom] = Feature.getRectWrapPoints(fPointArr);
                        childTotalHeight += Utils.getLenOfTwoPnts(leftTop, leftBottom); // 计算所有子元素的宽度之和
                    })
                    const spaceLen = (wrapHeight - childTotalHeight) / (features.length + 1)   // 每一段可分配空间
                    if (spaceLen < 0) return
                    features.sort((a, b) => Feature.getRectWrapExtent(a.pointArr)[3] - Feature.getRectWrapExtent(b.pointArr)[2])
                    this.toTopAlign(features);
                    let lastLen = spaceLen  // 之前所有的子元素宽度+之前所有分配的空间长度

                    features.forEach((f, i) => {
                        const prevFeature = features[i - 1];  // 上一个元素
                        if (prevFeature) {
                            const fPointArr = prevFeature.getPointArr(prevFeature.pointArr, -angle, center); // 获取旋转之前的点
                            const [leftTop, rightTop, rightBottom, leftBottom] = Feature.getRectWrapPoints(fPointArr);
                            lastLen += Utils.getLenOfTwoPnts(leftTop, leftBottom) + spaceLen;
                        }
                        const dx = lastLen * Math.sin(-angle * Math.PI / 180);
                        const dy = lastLen * Math.cos(-angle * Math.PI / 180);
                        f.translate(dx, dy)
                    })
                    break;
                }
            default:
                break;
        }
        return this;
    }
    /**
* 获得元素宽高比 
* @returns 
*/
    getRatio() {
        const { width, height } = this.getBboxSize()
        return width / height;
    }

    // 初始化设置包围盒水平方向与垂直方向的向量
    getVctor() {
        return {
            x: Utils.getVector(this.pointArr[0], this.pointArr[1]),
            y: Utils.getVector(this.pointArr[0], this.pointArr[3]),
        }
    }
}

export default Rect;